package com.fanrui.code.socket.netty.client;

import com.fanrui.code.config.Config;
import com.fanrui.code.util.logger.Stdout;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

public abstract class SocketClient {

	private String host;

	private int port;

	private Bootstrap bootstrap;

	private volatile Channel channel;

	public SocketClient(String host, int port) {
		this.host = host;
		this.port = port;
		this.bootstrap = createBootstrap();
	}

	private Bootstrap createBootstrap() {
		System.setProperty("io.netty.noUnsafe","true");
		EventLoopGroup worker = new NioEventLoopGroup();
		Bootstrap boot = new Bootstrap().group(worker).channel(NioSocketChannel.class).handler(getChannelInitializer())
				.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT).option(ChannelOption.TCP_NODELAY, true)
				.option(ChannelOption.SO_KEEPALIVE, true).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 60 * 1000);
		
		return boot;
	}

	/**
	 * 用于初始化Channel
	 * 
	 * @return
	 */
	protected abstract ChannelInitializer<Channel> getChannelInitializer();

	/**
	 * 
	 * 确保断线重连
	 * 
	 * @return
	 * @throws InterruptedException
	 */
	public Channel getConnect() throws InterruptedException {
		if (null == this.channel || !this.channel.isActive()) {
			synchronized (this.bootstrap) {
				if (null == this.channel || !this.channel.isActive()) {

					if (null != this.channel) {
						this.channel.close();
						this.channel = null;
					}

					do {
						Stdout.GET().info("{} 尝试建立连接 {}:{}", this.getClass().getSimpleName(), this.host, this.port);
						ChannelFuture future = this.bootstrap.connect(this.host, this.port).sync();

						if (future.isSuccess()) {
							Stdout.GET().info("{} 建立连接成功 {}:{}", this.getClass().getSimpleName(), this.host, this.port);
							this.channel = future.channel();
						} else {
							Stdout.GET().info("{} 建立连接失败 {}:{} ，稍后重试！", this.getClass().getSimpleName(), this.host,
									this.port);
							this.bootstrap.wait(Config.DEFAULT_TIMEOUTMILLS);
						}
					} while (this.channel == null);
				}
			}
		}

		return this.channel;
	}

	public void close() {
		if (null != this.channel) {
			this.channel.close();
			this.bootstrap.config().group().shutdownGracefully();
		}
	}
	public Channel tryConnect()  
	{         
	    try   
	    {  
	        ChannelFuture future = bootstrap.connect(host, port).sync();  
	        if(future.isSuccess())  
	        {  
	        	Stdout.GET().info("Connect to [" + host + ":" + port + "] successed.");  
	            return future.channel();  
	        }  
	        else  
	        {                 
	        	Stdout.GET().info("Connect to [" + host + ":" + port + "] failed.");  
	        	Stdout.GET().info("Try to reconnect in 10s.");  
	            Thread.sleep(10000);  
	            return null;  
	        }  
	    }   
	    catch (Exception exception)   
	    {  
	    	Stdout.GET().info("Connect to [" + host + ":" + port + "] failed.");  
	    	Stdout.GET().info("Try to reconnect in 10 seconds.");  
	        try   
	        {  
	            Thread.sleep(10000);  
	        }   
	        catch (InterruptedException e)   
	        {  
	            e.printStackTrace();  
	        }  
	        return null;  
	    }  
	}
}
